-- UTF-8 without BOM
local type = type
local string = string
local lower = string.lower
local error = error
local pairs = pairs
local table = table
local concat = table.concat
local ipairs = ipairs
local print = print
local open = io.open
local tostring = tostring
local arg = {...}

local jdk7 = false -- false for jdk6
namespace = arg[1] -- for bean namespace
local namespace = namespace
if not namespace then error("ERROR: arg[1] must be namespace") end

local template_hint = "// This file is generated by genbeans tool. Do NOT edit it! @formatter:off\n"
local template_bean = template_hint .. [=[
package ]=] .. namespace .. [=[.bean;
#<#
import java.lang.reflect.Field;#>#
import #(bean.imports);
#(bean.comment)
public final class #(bean.name) extends Bean<#(bean.name)>
{
	private static final long serialVersionUID = #(bean.uid);
	public  static final int BEAN_TYPE = #(bean.type);
	public  static final #(bean.name) BEAN_STUB = new #(bean.name)();#(bean.pool_def)
#{#	public  static final #(var.type) #(var.name)#(var.value);#(var.comment2)
#}##(##(var.field)#)#
#(#	private /*#(var.id3)*/ #(var.final)#(var.type) #(var.name);#(var.comment2)
#)##<#
	static
	{
		try
		{
			Class<#(bean.name)> c = #(bean.name).class;
#(##(var.fieldget)#)#		}
		catch(Exception e)
		{
			throw new Error(e);
		}
	}

	public #(bean.name)()
	{
#(##(var.new)#)#	}

	public #(bean.name)(#(##(var.type_i) #(var.name), #)#)
	{
#(#		#(var.init);
#)#	}

#>#	@Override
	public void reset()
	{
#(#		#(var.reset);
#)#	}

	#(bean.param_warning)@Override
	public void assign(#(bean.name) b)
	{#<#
		if(b == this) return;
		if(b == null) { reset(); return; }#>#
#(#		#(var.assign);
#)#	}
#(#
	/** @return #(var.comment1) */
	public #(var.type) get#(var.name_u)()
	{
		return #(var.name);
	}
#(var.set)#)#
	@Override
	public int type()
	{
		return #(bean.type);
	}

	@Override
	public #(bean.name) stub()
	{
		return BEAN_STUB;
	}

	@Override
	public #(bean.name) create()
	{
		return new #(bean.name)();
	}

	@Override
	public int initSize()
	{
		return #(bean.initsize);
	}

	@Override
	public int maxSize()
	{
		return #(bean.maxsize);
	}
#(bean.pool_func)
	@Override
	public OctetsStream marshal(OctetsStream s)
	{
#(##(var.marshal)#)#		return s.marshal1((byte)0);
	}

	@Override
	public OctetsStream unmarshal(OctetsStream s) throws MarshalException
	{
		for(;;) { int i = s.unmarshalInt1() & 0xff, t = i & 3; if(i > 251) i += s.unmarshalInt1() << 2; switch(i >> 2)
		{
			case 0: return s;
#(##(var.unmarshal)#)#			default: s.unmarshalSkipVar(t);
		}}
	}

	@Override
	public #(bean.name) clone()
	{
		return new #(bean.name)(#(##(var.name), #)#);
	}

	@Override
	public int hashCode()
	{
		int h = (int)serialVersionUID;
#(#		h = h * 31 + 1 + #(var.hashcode);
#)#		return h;
	}

	@Override
	public boolean equals(Object o)
	{
		if(o == this) return true;
		if(!(o instanceof #(bean.name))) return false;#<#
		#(bean.name) b = (#(bean.name))o;#>#
#(#		if(#(var.equals)) return false;
#)#		return true;
	}

	@Override
	public int compareTo(#(bean.name) b)
	{
		if(b == this) return 0;
		if(b == null) return 1;#<#
		int c;#>#
#(#		c = #(var.compareto); if(c != 0) return c;
#)#		return 0;
	}

	@Override
	public String toString()
	{
		StringBuilder s = new StringBuilder(16 + #(bean.initsize) * 2).append('{');#<#
#(#		#(var.tostring);
#)#		s.setLength(s.length() - 1);#>#
		return s.append('}').toString();
	}

	@Override
	public StringBuilder toJson(StringBuilder s)
	{
		if(s == null) s = new StringBuilder(1024);
		s.append('{');#<#
#(##(var.tojson)#)#		s.setLength(s.length() - 1);#>#
		return s.append('}');
	}

	@Override
	public StringBuilder toLua(StringBuilder s)
	{
		if(s == null) s = new StringBuilder(1024);
		s.append('{');#<#
#(##(var.tolua)#)#		s.setLength(s.length() - 1);#>#
		return s.append('}');
	}

	@Override
	public Safe safe(SContext.Safe<?> parent)
	{
		return new Safe(this, parent);
	}

	@Override
	public Safe safe()
	{
		return new Safe(this, null);
	}

	public static final class Safe extends SContext.Safe<#(bean.name)>
	{
#(##(var.safecache)#)#
		private Safe(#(bean.name) bean, SContext.Safe<?> parent)
		{
			super(bean, parent);
		}
#(##(var.getsafe)#(var.setsafe)#)#	}
}
]=]

local template_rpcbean = template_hint .. [=[
package ]=] .. namespace .. [=[.bean;

import jane.core.RpcBean;
#(bean.comment)
public final class #(bean.name) extends RpcBean<#(bean.arg), #(bean.res), #(bean.name)>
{
	private static final long serialVersionUID = #(bean.uid);
	public  static final #(bean.name) BEAN_STUB = new #(bean.name)();
	public #(bean.name)() {}
	public #(bean.name)(#(bean.arg) a) { _arg = a; }
	@Override public int type() { return #(bean.type); }
	@Override public #(bean.name) stub() { return BEAN_STUB; }
	@Override public #(bean.name) create() { return new #(bean.name)(); }
	@Override public #(bean.arg) createArg() { return new #(bean.arg)(); }
	@Override public #(bean.res) createRes() { return new #(bean.res)(); }
}
]=]

local template_allbeans = template_hint .. [=[
package ]=] .. namespace .. [=[.bean;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import jane.core.Bean;#<#
import jane.core.BeanHandler;
import jane.core.IntMap;#>#

/** 全部beans的注册(自动生成的静态类) */
public final class AllBeans
{
	private AllBeans() {}

	/** 获取全部的bean实例 */
	public static Collection<Bean<?>> getAllBeans()
	{
		List<Bean<?>> r = new ArrayList<]=] .. (jdk7 and "" or "Bean<?>") .. [=[>(#(bean.count));
#(#		r.add(#(bean.name).BEAN_STUB);
#)#		return r;
	}
#[#
	public static IntMap<BeanHandler<?>> get#(hdl.name)Handlers()
	{
		IntMap<BeanHandler<?>> r = new IntMap<]=] .. (jdk7 and "" or "BeanHandler<?>") .. [=[>(#(hdl.count) * 4);
#(#		r.put(#(bean.type), new #(hdl.path).#(bean.name)Handler());
#)#		return r;
	}
#]#}
]=]

local template_bean_handler = [=[
package #(hdl.path);

import org.apache.mina.core.session.IoSession;
import jane.core.BeanHandler;
import jane.core.Log;
import jane.core.NetManager;
import ]=] .. namespace .. [=[.bean.#(bean.name);

public final class #(bean.name)Handler extends BeanHandler<#(bean.name)>
{
	/*\
#(#	|*| #(var.type) #(var.name)#(var.value);#(var.comment2)
#)#	\*/

	@Override
	public void onProcess(final NetManager manager, final IoSession session, final #(bean.name) arg)
	{
		Log.log.debug("{}.onProcess: arg={}", getClass().getName(), arg);
	}
}
]=]

local template_rpc_handler = [=[
package #(hdl.path);

import org.apache.mina.core.session.IoSession;
import jane.core.Log;
import jane.core.NetManager;
import jane.core.RpcHandler;
import ]=] .. namespace .. [=[.bean.#(bean_arg.name);#<#
import ]=] .. namespace .. [=[.bean.#(bean_res.name);#>#
import ]=] .. namespace .. [=[.bean.#(bean.name);#>#

public final class #(bean.name)Handler extends RpcHandler<#(bean_arg.name), #(bean_res.name), #(bean.name)>
{
	/*\
#(#	|*| #(var.type) #(var.name)#(var.value);#(var.comment2)
#)#	\*/
	/*\
#(#	|*| #(var.type) #(var.name)#(var.value);#(var.comment2)
#)#	\*/

	@Override
	public boolean onServer(final NetManager manager, final IoSession session, final #(bean.name) rpcBean)
	{
		final #(bean_arg.name) arg = rpcBean.getArg();
		//final #(bean_res.name) res = rpcBean.getRes();
		Log.log.debug("{}: onServer: {}", getClass().getName(), arg);
		return true;
	}

	@Override
	public void onClient(final NetManager manager, final IoSession session, final #(bean.name) rpcBean)
	{
		final #(bean_arg.name) arg = rpcBean.getArg();
		final #(bean_res.name) res = rpcBean.getRes();
		Log.log.debug("{}: onClient: arg={},res={}", getClass().getName(), arg, res);
	}

	@Override
	public void onTimeout(final NetManager manager, final IoSession session, final #(bean.name) rpcBean)
	{
		final #(bean_arg.name) arg = rpcBean.getArg();
		Log.log.debug("{}: onTimeout: {}", getClass().getName(), arg);
	}
}
]=]

local template_alltables = template_hint .. [=[
package ]=] .. namespace .. [=[.bean;

import #(tables.imports);

/** 全部的数据库表的注册和使用类(自动生成的静态类) */
public final class AllTables
{
	private AllTables() {}
	private static final DBManager _dbm = DBManager.instance();
	/**
	 * 注册全部的数据库表<p>
	 * 用于初始化和注册下面的全部静态成员(保持和AllBeans.register一致的用法), 并启动提交线程<br>
	 * 调用前要先初始化数据库管理器: DBManager.instance().startup(...)
	 */
	public static void register() { _dbm.startCommitThread(); }#<#
#>#
#(#	#(table.comment)public static final #(table.table)<#(table.key)#(table.comma)#(table.value), #(table.value).Safe> #(table.name) = _dbm.<#(table.key)#(table.comma)#(table.value), #(table.value).Safe>openTable(#(table.id), "#(table.name)", "#(table.lock)", #(table.cachesize)#(table.comma)#(table.keys), #(table.values));
#)#
	/**
	 * 以下内部类可以单独使用,避免初始化前面的表对象,主要用于获取表的键值类型
	 */
	public static final class Types
	{
		public static HashMap<String, Bean<?>> getKeyTypes()
		{
			HashMap<String, Bean<?>> r = new HashMap<]=] .. (jdk7 and "" or "String, Bean<?>") .. [=[>(#(tables.count) * 2);
#(#			r.put("#(table.name)", #(table.keyg));
#)#			return r;
		}

		public static HashMap<String, Bean<?>> getValueTypes()
		{
			HashMap<String, Bean<?>> r = new HashMap<]=] .. (jdk7 and "" or "String, Bean<?>") .. [=[>(#(tables.count) * 2);
#(#			r.put("#(table.name)", #(table.values));
#)#			return r;
		}
	}
}
]=]

local typedef = {}
local function merge(ta, tb)
	local r = {}
	for k, v in pairs(ta) do r[k] = v end
	for k, v in pairs(tb) do r[k] = v end
	return r
end
local function typename(var, t)
	local def = typedef[t]
	if not def then return t end
	if type(def) == "function" then
		local t = { id = 0, import = {} } def(t, 0) def = t
		for k in pairs(t.import) do var.import[k] = true end
	end
	if type(def) == "table" then return type(def.type) == "string" and def.type or def.type(var) end
	error("ERROR: unknown typename(" .. var .. ", " .. t .. ")")
end
local function subtypename(var, t)
	local def = typedef[t]
	if not def then return t end
	if type(def) == "function" then
		local t = { id = 0, import = {} } def(t, 0) def = t
		for k in pairs(t.import) do var.import[k] = true end
	end
	if type(def) == "table" then return type(def.type_o) == "string" and def.type_o or def.type_o(var) end
	error("ERROR: unknown subtypename(" .. var .. ", " .. t .. ")")
end
local function subtypename_safe(var, t)
	return typedef[t] and subtypename(var, t) or (t .. ".Safe")
end
local function subtypename_new(var, t)
	if not var then return jdk7 and "" or ", " end
	return jdk7 and "" or subtypename(var, t)
end
local function subtypeid(t)
	local def = typedef[t]
	if not def then def = typedef.bean end
	if type(def) == "function" then local t = { id = 0, import = {} } def(t, 0) return t.subtypeid end
	if type(def) == "table" then return def.subtypeid end
	error("ERROR: unknown subtypeid(" .. t .. ")")
end
local function get_unmarshal_kv(var, kv, t)
	local s = (typedef[var[kv]] or typedef.bean).unmarshal_kv
	return type(s) == "string" and s or s(var, kv, t)
end
typedef.byte =
{
	import = { "jane.core.SBase" },
	name_u = function(var) return var.name:sub(1, 1):upper() .. var.name:sub(2) end,
	type = "byte", type_i = "byte", type_o = "Byte",
	subtypeid = 0,
	final = "",
	field = "\tprivate static Field FIELD_#(var.name);\n",
	fieldget = "\t\t\tFIELD_#(var.name) = c.getDeclaredField(\"#(var.name)\"); FIELD_#(var.name).setAccessible(true);\n",
	safecache = "",
	new = "",
	init = "this.#(var.name) = #(var.name)",
	reset = "#(var.name) = 0",
	assign = "this.#(var.name) = b.#(var.name)",
	set = [[

	/** @param #(var.name) #(var.comment1) */
	public void set#(var.name_u)(#(var.type) #(var.name))
	{
		this.#(var.name) = #(var.name);
	}
]],
	getsafe = [[

		/** @return #(var.comment1) */
		public #(var.type) get#(var.name_u)()
		{
			return _bean.#(var.name);
		}
]],
	setsafe = [[

		/** @param #(var.name) #(var.comment1) */
		public void set#(var.name_u)(#(var.type) #(var.name))
		{
			if(initSContext()) _sCtx.addOnRollback(new SBase.S#(var.type_o)(_bean, FIELD_#(var.name), _bean.#(var.name)));
			_bean.#(var.name) = #(var.name);
		}
]],
	marshal = function(var)
		return var.id < 63 and
			string.format("\t\tif(this.#(var.name) != 0) s.marshal1((byte)0x%02x).marshal(this.#(var.name));\n", var.id * 4) or
			string.format("\t\tif(this.#(var.name) != 0) s.marshal2(0x%04x).marshal(this.#(var.name));\n", 0xfc00 + var.id - 63)
	end,
	unmarshal = "\t\t\tcase #(var.id): this.#(var.name) = (#(var.type))s.unmarshalInt(t); break;\n",
	unmarshal_kv = function(var, kv, t) if kv then return "(" .. typename(var, var[kv]) .. ")s.unmarshalIntKV(" .. t .. ")" end end,
	hashcode = "this.#(var.name)",
	equals = "this.#(var.name) != b.#(var.name)",
	compareto = "this.#(var.name) - b.#(var.name)",
	tostring = "s.append(this.#(var.name)).append(',')",
	tojson = "\t\ts.append(\"\\\"#(var.name)\\\":\").append(this.#(var.name)).append(',');\n",
	tolua = "\t\ts.append(\"#(var.name)=\").append(this.#(var.name)).append(',');\n",
}
typedef.char  = merge(typedef.byte, { type = "char",  type_i = "char",  type_o = "Char"  })
typedef.short = merge(typedef.byte, { type = "short", type_i = "short", type_o = "Short" })
typedef.int = merge(typedef.byte,
{
	type = "int",
	type_i = "int",
	type_o = "Integer",
	unmarshal = "\t\t\tcase #(var.id): this.#(var.name) = s.unmarshalInt(t); break;\n",
	unmarshal_kv = function(var, kv, t) if kv then return "s.unmarshalIntKV(" .. t .. ")" end end,
})
typedef.long = merge(typedef.byte,
{
	type = "long",
	type_i = "long",
	type_o = "Long",
	unmarshal = "\t\t\tcase #(var.id): this.#(var.name) = s.unmarshalLong(t); break;\n",
	unmarshal_kv = function(var, kv, t) if kv then return "s.unmarshalLongKV(" .. t .. ")" end end,
	hashcode = "(int)this.#(var.name)",
	compareto = "Long.signum(this.#(var.name) - b.#(var.name))",
})
typedef.bool = merge(typedef.byte,
{
	type = "boolean", type_i = "boolean", type_o = "Boolean",
	reset = "#(var.name) = false",
	marshal = function(var)
		return var.id < 63 and
			string.format("\t\tif(this.#(var.name)) s.marshal2(0x%04x);\n", var.id * 0x400 + 1) or
			string.format("\t\tif(this.#(var.name)) s.marshal3(0x%06x);\n", 0xfc0001 + (var.id - 63) * 0x100)
	end,
	unmarshal = "\t\t\tcase #(var.id): this.#(var.name) = (s.unmarshalInt(t) != 0); break;\n",
	unmarshal_kv = function(var, kv, t) if kv then return "(s.unmarshalIntKV(" .. t .. ") != 0)" end end,
	hashcode = "(this.#(var.name) ? 0xcafebabe : 0xdeadbeef)",
	compareto = "(this.#(var.name) == b.#(var.name) ? 0 : (this.#(var.name) ? 1 : -1))",
})
typedef.float = merge(typedef.byte,
{
	type = "float", type_i = "float", type_o = "Float",
	subtypeid = 4,
	marshal = function(var)
		return var.id < 63 and
			string.format("\t\tif(this.#(var.name) != 0) s.marshal2(0x%04x).marshal(this.#(var.name));\n", var.id * 0x400 + 0x308) or
			string.format("\t\tif(this.#(var.name) != 0) s.marshal3(0x%06x).marshal(this.#(var.name));\n", 0xff0008 + (var.id - 63) * 0x100)
	end,
	unmarshal = "\t\t\tcase #(var.id): this.#(var.name) = s.unmarshalFloat(t); break;\n",
	unmarshal_kv = function(var, kv, t) if kv then return "s.unmarshalFloatKV(" .. t .. ")" end end,
	hashcode = "Float.floatToRawIntBits(this.#(var.name))",
	compareto = "Float.compare(this.#(var.name), b.#(var.name))",
})
typedef.double = merge(typedef.byte,
{
	type = "double", type_i = "double", type_o = "Double",
	subtypeid = 5,
	marshal = function(var)
		return var.id < 63 and
			string.format("\t\tif(this.#(var.name) != 0) s.marshal2(0x%04x).marshal(this.#(var.name));\n", var.id * 0x400 + 0x309) or
			string.format("\t\tif(this.#(var.name) != 0) s.marshal3(0x%06x).marshal(this.#(var.name));\n", 0xff0009 + (var.id - 63) * 0x100)
	end,
	unmarshal = "\t\t\tcase #(var.id): this.#(var.name) = s.unmarshalDouble(t); break;\n",
	unmarshal_kv = function(var, kv, t) if kv then return "s.unmarshalDoubleKV(" .. t .. ")" end end,
	hashcode = "(int)((Double.doubleToRawLongBits(this.#(var.name)) * 0x100000001L) >> 32)",
	compareto = "Double.compare(this.#(var.name), b.#(var.name))",
})
typedef.string = merge(typedef.byte,
{
	import = { "jane.core.Util", "jane.core.SBase" },
	type = "String", type_i = "String", type_o = "String",
	subtypeid = 1,
	new = "\t\t#(var.name) = \"\";\n",
	init = "this.#(var.name) = (#(var.name) != null ? #(var.name) : \"\")",
	reset = "#(var.name) = \"\"",
	assign = "this.#(var.name) = (b.#(var.name) != null ? b.#(var.name) : \"\")",
	set = [[

	/** @param #(var.name) #(var.comment1) */
	public void set#(var.name_u)(#(var.type) #(var.name))
	{
		this.#(var.name) = (#(var.name) != null ? #(var.name) : "");
	}
]],
	setsafe = [[

		/** @param #(var.name) #(var.comment1) */
		public void set#(var.name_u)(#(var.type) #(var.name))
		{
			if(initSContext()) _sCtx.addOnRollback(new SBase.SObject(_bean, FIELD_#(var.name), _bean.#(var.name)));
			_bean.#(var.name) = (#(var.name) != null ? #(var.name) : "");
		}
]],
	marshal = function(var)
		return var.id < 63 and
			string.format("\t\tif(!this.#(var.name).isEmpty()) s.marshal1((byte)0x%02x).marshal(this.#(var.name));\n", var.id * 4 + 1) or
			string.format("\t\tif(!this.#(var.name).isEmpty()) s.marshal2(0x%04x).marshal(this.#(var.name));\n", 0xfd00 + var.id - 63)
	end,
	unmarshal = "\t\t\tcase #(var.id): this.#(var.name) = s.unmarshalString(t); break;\n",
	unmarshal_kv = function(var, kv, t) if kv then return "s.unmarshalStringKV(" .. t .. ")" end end,
	hashcode = "this.#(var.name).hashCode()",
	equals = "!this.#(var.name).equals(b.#(var.name))",
	compareto = "this.#(var.name).compareTo(b.#(var.name))",
	tojson = "\t\tUtil.toJStr(s.append(\"\\\"#(var.name)\\\":\"), this.#(var.name)).append(',');\n",
	tolua = "\t\tUtil.toJStr(s.append(\"#(var.name)=\"), this.#(var.name)).append(',');\n",
})
typedef.octets = merge(typedef.string,
{
	import = { "jane.core.Octets", "jane.core.DynBean", "jane.core.SBase" },
	type = "Octets", type_i = "Octets", type_o = "Octets",
	new = "\t\t#(var.name) = new Octets(#(var.cap));\n",
	init = "this.#(var.name) = (#(var.name) != null ? #(var.name) : new Octets(#(var.cap)))",
	reset = "#(var.name).clear()",
	assign = "if(b.#(var.name) != null) this.#(var.name).replace(b.#(var.name)); else this.#(var.name).clear()",
	set = [[

	/** @param #(var.name) #(var.comment1) */
	public void set#(var.name_u)(#(var.type) #(var.name))
	{
		this.#(var.name) = (#(var.name) != null ? #(var.name) : new Octets(#(var.cap)));
	}

	/** #(var.comment1) */
	public <B extends Bean<B>> void marshal#(var.name_u)(Bean<B> b)
	{
		OctetsStream os = OctetsStream.wrap(this.#(var.name));
		os.resize(0);
		os.reserve(b.initSize());
		this.#(var.name) = os;
		b.marshal(os);
	}

	/** #(var.comment1) */
	public <B extends Bean<B>> Bean<B> unmarshal#(var.name_u)(Bean<B> b) throws MarshalException
	{
		b.unmarshal(OctetsStream.wrap(this.#(var.name)));
		return b;
	}

	/** #(var.comment1) */
	public DynBean unmarshal#(var.name_u)() throws MarshalException
	{
		DynBean b = new DynBean();
		b.unmarshal(OctetsStream.wrap(this.#(var.name)));
		return b;
	}
]],
	getsafe = [[

		/** @return #(var.comment1) */
		public #(var.type) get#(var.name_u)()
		{
			return _bean.#(var.name).clone();
		}

		/** @param #(var.name) #(var.comment1) */
		public void set#(var.name_u)(#(var.type) #(var.name))
		{
			if(initSContext()) _sCtx.addOnRollback(new SBase.SOctets(_bean, FIELD_#(var.name), _bean.#(var.name), false));
			_bean.#(var.name) = (#(var.name) != null ? #(var.name).clone() : new Octets(#(var.cap)));
		}

		/** #(var.comment1) */
		public byte[] copyOf#(var.name_u)()
		{
			return _bean.#(var.name).getBytes();
		}

		/** #(var.comment1) */
		public <B extends Bean<B>> void marshal#(var.name_u)(Bean<B> b)
		{
			if(initSContext()) _sCtx.addOnRollback(new SBase.SOctets(_bean, FIELD_#(var.name), _bean.#(var.name), false));
			_bean.#(var.name) = b.marshal(new OctetsStream(b.initSize()));
		}

		/** #(var.comment1) */
		public <B extends Bean<B>> Bean<B> unmarshal#(var.name_u)(Bean<B> b) throws MarshalException
		{
			return _bean.unmarshal#(var.name_u)(b);
		}

		/** #(var.comment1) */
		public DynBean unmarshal#(var.name_u)() throws MarshalException
		{
			return _bean.unmarshal#(var.name_u)();
		}

		/** @return #(var.comment1) */
		@Deprecated
		public #(var.type) unsafe#(var.name_u)()
		{
			return _bean.#(var.name);
		}
]],
	setsafe = "",
	marshal = function(var)
		return var.id < 63 and
			string.format("\t\tif(!this.#(var.name).empty()) s.marshal1((byte)0x%02x).marshal(this.#(var.name));\n", var.id * 4 + 1) or
			string.format("\t\tif(!this.#(var.name).empty()) s.marshal2(0x%04x).marshal(this.#(var.name));\n", 0xfd00 + var.id - 63)
	end,
	unmarshal = "\t\t\tcase #(var.id): s.unmarshal(this.#(var.name), t); break;\n",
	unmarshal_kv = function(var, kv, t) if kv then return "s.unmarshalOctetsKV(" .. t .. ")" end end,
	tojson = "\t\tthis.#(var.name).dumpJStr(s.append(\"\\\"#(var.name)\\\":\")).append(',');\n",
	tolua = "\t\tthis.#(var.name).dumpJStr(s.append(\"#(var.name)=\")).append(',');\n",
})
typedef.vector = merge(typedef.octets,
{
	import = { "java.util.ArrayList", "java.util.Collection", "jane.core.Util", "jane.core.SList" },
	type = function(var) return "ArrayList<" .. subtypename(var, var.k) .. ">" end,
	type_i = function(var) return "Collection<" .. subtypename(var, var.k) .. ">" end,
	stype = function(var) return "SList<" .. subtypename(var, var.k) .. ", " .. subtypename_safe(var, var.k) .. ">" end,
	final = "final ",
	field = "",
	fieldget = "",
	new = function(var) return "\t\t#(var.name) = new ArrayList<" .. subtypename_new(var, var.k) .. ">(#(var.cap));\n" end,
	init = function(var) return "Util.appendDeep(#(var.name), this.#(var.name) = new ArrayList<" .. subtypename_new(var, var.k) .. ">(#(var.cap)))" end,
	assign = "this.#(var.name).clear(); Util.appendDeep(b.#(var.name), this.#(var.name))",
	set = "",
	getsafe = [[

		/** @return #(var.comment1) */
		public #(var.stype) get#(var.name_u)()
		{
			return new #(var.stype)(this, _bean.#(var.name));
		}

		/** @return #(var.comment1) */
		@Deprecated
		public #(var.type) unsafe#(var.name_u)()
		{
			return _bean.#(var.name);
		}
]],
	marshal = function(var)
		return var.id < 63 and
			string.format([[		if(!this.#(var.name).isEmpty())
		{
			s.marshal2(0x%04x).marshalUInt(this.#(var.name).size());
			for(%s v : this.#(var.name))
				s.marshal(v);
		}
]], var.id * 0x400 + 0x300 + subtypeid(var.k), subtypename(var, var.k)) or
			string.format([[		if(!this.#(var.name).isEmpty())
		{
			s.marshal3(0x%06x).marshalUInt(this.#(var.name).size());
			for(%s v : this.#(var.name))
				s.marshal(v);
		}
]], 0xff0000 + (var.id - 63) * 0x100 + subtypeid(var.k), subtypename(var, var.k))
	end,
	unmarshal = function(var) return string.format([[			case #(var.id):
			{
				this.#(var.name).clear();
				if(t != 3) { s.unmarshalSkipVar(t); break; }
				t = s.unmarshalInt1();
				if((t >> 3) != 0) { s.unmarshalSkipVarSub(t); break; }
				t &= 7;
				int n = s.unmarshalUInt();
				this.#(var.name).ensureCapacity(n < 0x10000 ? n : 0x10000);
				for(; n > 0; --n)
					this.#(var.name).add(%s);
			} break;
]], get_unmarshal_kv(var, "k", "t")) end,
	compareto = "Util.compareTo(this.#(var.name), b.#(var.name))",
	tostring = "Util.append(s, this.#(var.name))",
	tojson = "\t\tUtil.appendJson(s.append(\"\\\"#(var.name)\\\":\"), this.#(var.name));\n",
	tolua = "\t\tUtil.appendLua(s.append(\"#(var.name)=\"), this.#(var.name));\n",
})
typedef.list = merge(typedef.vector,
{
	import = { "java.util.LinkedList", "java.util.Collection", "jane.core.Util", "jane.core.SList" },
	type = function(var) return "LinkedList<" .. subtypename(var, var.k) .. ">" end,
	new = function(var) return "\t\t#(var.name) = new LinkedList<" .. subtypename_new(var, var.k) .. ">();\n" end,
	init = function(var) return "Util.appendDeep(#(var.name), this.#(var.name) = new LinkedList<" .. subtypename_new(var, var.k) .. ">())" end,
	unmarshal = function(var) return string.format([[			case #(var.id):
			{
				this.#(var.name).clear();
				if(t != 3) { s.unmarshalSkipVar(t); break; }
				t = s.unmarshalInt1();
				if((t >> 3) != 0) { s.unmarshalSkipVarSub(t); break; }
				t &= 7;
				for(int n = s.unmarshalUInt(); n > 0; --n)
					this.#(var.name).add(%s);
			} break;
]], get_unmarshal_kv(var, "k", "t")) end,
})
typedef.deque = merge(typedef.list,
{
	import = { "java.util.ArrayDeque", "java.util.Collection", "jane.core.Util", "jane.core.SDeque" },
	type = function(var) return "ArrayDeque<" .. subtypename(var, var.k) .. ">" end,
	stype = function(var) return "SDeque<" .. subtypename(var, var.k) .. ", " .. subtypename_safe(var, var.k) .. ">" end,
	new = function(var) return "\t\t#(var.name) = new ArrayDeque<" .. subtypename_new(var, var.k) .. ">(#(var.cap));\n" end,
	init = function(var) return "Util.appendDeep(#(var.name), this.#(var.name) = new ArrayDeque<" .. subtypename_new(var, var.k) .. ">(#(var.cap)))" end,
})
typedef.hashset = merge(typedef.list,
{
	import = { "java.util.HashSet", "java.util.Collection", "jane.core.Util", "jane.core.SSet", "jane.core.SSet.SSetListener" },
	type = function(var) return "HashSet<" .. subtypename(var, var.k) .. ">" end,
	stype = function(var) return "SSet<" .. subtypename(var, var.k) .. ", " .. subtypename_safe(var, var.k) .. ">" end,
	field = function(var) return [[
	private static SSetListener<]] .. subtypename(var, var.k) .. [[> LISTENER_#(var.name);
]] end,
	safecache = "\t\tprivate #(var.stype) CACHE_#(var.name);\n",
	new = function(var) return "\t\t#(var.name) = new HashSet<" .. subtypename_new(var, var.k) .. ">(#(var.cap));\n" end,
	init = function(var) return "Util.appendDeep(#(var.name), this.#(var.name) = new HashSet<" .. subtypename_new(var, var.k) .. ">(#(var.cap)))" end,
	getsafe = function(var) return [[

		/** #(var.comment1) */
		public static void onListener#(var.name_u)(SSetListener<]] .. subtypename(var, var.k) .. [[> listener)
		{
			LISTENER_#(var.name) = listener;
		}

		/** @return #(var.comment1) */
		public #(var.stype) get#(var.name_u)()
		{
			if(CACHE_#(var.name) == null) CACHE_#(var.name) = new #(var.stype)(this, _bean.#(var.name), LISTENER_#(var.name));
			return CACHE_#(var.name);
		}

		/** @return #(var.comment1) */
		@Deprecated
		public #(var.type) unsafe#(var.name_u)()
		{
			return _bean.#(var.name);
		}
]] end,
})
typedef.treeset = merge(typedef.hashset,
{
	import = { "java.util.TreeSet", "java.util.Collection", "jane.core.Util", "jane.core.SSSet", "jane.core.SSet.SSetListener" },
	type = function(var) return "TreeSet<" .. subtypename(var, var.k) .. ">" end,
	stype = function(var) return "SSSet<" .. subtypename(var, var.k) .. ", " .. subtypename_safe(var, var.k) .. ">" end,
	new = function(var) return "\t\t#(var.name) = new TreeSet<" .. subtypename_new(var, var.k) .. ">();\n" end,
	init = function(var) return "Util.appendDeep(#(var.name), this.#(var.name) = new TreeSet<" .. subtypename_new(var, var.k) .. ">())" end,
})
typedef.linkedhashset = merge(typedef.hashset,
{
	import = { "java.util.LinkedHashSet", "java.util.Collection", "jane.core.Util", "jane.core.SSet", "jane.core.SSet.SSetListener" },
	type = function(var) return "LinkedHashSet<" .. subtypename(var, var.k) .. ">" end,
	new = function(var) return "\t\t#(var.name) = new LinkedHashSet<" .. subtypename_new(var, var.k) .. ">(#(var.cap));\n" end,
	init = function(var) return "Util.appendDeep(#(var.name), this.#(var.name) = new LinkedHashSet<" .. subtypename_new(var, var.k) .. ">(#(var.cap)))" end,
})
typedef.hashmap = merge(typedef.list,
{
	import = { "java.util.HashMap", "java.util.Map.Entry", "java.util.Map", "jane.core.Util", "jane.core.SMap", "jane.core.SMap.SMapListener" },
	type = function(var) return "HashMap<" .. subtypename(var, var.k) .. ", " .. subtypename(var, var.v) .. ">" end,
	type_i = function(var) return "Map<" .. subtypename(var, var.k) .. ", " .. subtypename(var, var.v) .. ">" end,
	stype = function(var) return "SMap<" .. subtypename(var, var.k) .. ", " .. subtypename(var, var.v) .. ", " .. subtypename_safe(var, var.v) .. ">" end,
	field = function(var) return [[
	private static SMapListener<]] .. subtypename(var, var.k) .. ", " .. subtypename(var, var.v) .. [[> LISTENER_#(var.name);
]] end,
	safecache = "\t\tprivate #(var.stype) CACHE_#(var.name);\n",
	new = function(var) return "\t\t#(var.name) = new HashMap<" .. subtypename_new(var, var.k) .. subtypename_new() .. subtypename_new(var, var.v) .. ">(#(var.cap));\n" end,
	init = function(var) return "Util.appendDeep(#(var.name), this.#(var.name) = new HashMap<" .. subtypename_new(var, var.k) .. subtypename_new() .. subtypename_new(var, var.v) .. ">(#(var.cap)))" end,
	getsafe = function(var) return [[

		/** #(var.comment1) */
		public static void onListener#(var.name_u)(SMapListener<]] .. subtypename(var, var.k) .. ", " .. subtypename(var, var.v) .. [[> listener)
		{
			LISTENER_#(var.name) = listener;
		}

		/** @return #(var.comment1) */
		public #(var.stype) get#(var.name_u)()
		{
			if(CACHE_#(var.name) == null) CACHE_#(var.name) = new #(var.stype)(this, _bean.#(var.name), LISTENER_#(var.name));
			return CACHE_#(var.name);
		}

		/** @return #(var.comment1) */
		@Deprecated
		public #(var.type) unsafe#(var.name_u)()
		{
			return _bean.#(var.name);
		}
]] end,
	marshal = function(var)
		return var.id < 63 and
			string.format([[		if(!this.#(var.name).isEmpty())
		{
			s.marshal2(0x%04x).marshalUInt(this.#(var.name).size());
			for(Entry<%s, %s> e : this.#(var.name).entrySet())
				s.marshal(e.getKey()).marshal(e.getValue());
		}
]], var.id * 0x400 + 0x340 + subtypeid(var.k) * 8 + subtypeid(var.v), subtypename(var, var.k), subtypename(var, var.v)) or
			string.format([[		if(!this.#(var.name).isEmpty())
		{
			s.marshal3(0x%06x).marshalUInt(this.#(var.name).size());
			for(Entry<%s, %s> e : this.#(var.name).entrySet())
				s.marshal(e.getKey()).marshal(e.getValue());
		}
]], 0xff0040 + (var.id - 63) * 0x100 + subtypeid(var.k) * 8 + subtypeid(var.v), subtypename(var, var.k), subtypename(var, var.v))
	end,
	unmarshal = function(var) return string.format([[			case #(var.id):
			{
				this.#(var.name).clear();
				if(t != 3) { s.unmarshalSkipVar(t); break; }
				t = s.unmarshalInt1();
				if((t >> 6) != 1) { s.unmarshalSkipVarSub(t); break; }
				int k = (t >> 3) & 7; t &= 7;
				for(int n = s.unmarshalUInt(); n > 0; --n)
					this.#(var.name).put(%s, %s);
			} break;
]], get_unmarshal_kv(var, "k", "k"), get_unmarshal_kv(var, "v", "t")) end,
})
typedef.treemap = merge(typedef.hashmap,
{
	import = { "java.util.TreeMap", "java.util.Map.Entry", "java.util.Map", "jane.core.Util", "jane.core.SSMap", "jane.core.SMap.SMapListener" },
	type = function(var) return "TreeMap<" .. subtypename(var, var.k) .. ", " .. subtypename(var, var.v) .. ">" end,
	stype = function(var) return "SSMap<" .. subtypename(var, var.k) .. ", " .. subtypename(var, var.v) .. ", " .. subtypename_safe(var, var.v) .. ">" end,
	new = function(var) return "\t\t#(var.name) = new TreeMap<" .. subtypename_new(var, var.k) .. subtypename_new() .. subtypename_new(var, var.v) .. ">();\n" end,
	init = function(var) return "Util.appendDeep(#(var.name), this.#(var.name) = new TreeMap<" .. subtypename_new(var, var.k) .. subtypename_new() .. subtypename_new(var, var.v) .. ">())" end,
})
typedef.linkedhashmap = merge(typedef.hashmap,
{
	import = { "java.util.LinkedHashMap", "java.util.Map.Entry", "java.util.Map", "jane.core.Util", "jane.core.SMap", "jane.core.SMap.SMapListener" },
	type = function(var) return "LinkedHashMap<" .. subtypename(var, var.k) .. ", " .. subtypename(var, var.v) .. ">" end,
	new = function(var) return "\t\t#(var.name) = new LinkedHashMap<" .. subtypename_new(var, var.k) .. subtypename_new() .. subtypename_new(var, var.v) .. ">(#(var.cap));\n" end,
	init = function(var) return "Util.appendDeep(#(var.name), this.#(var.name) = new LinkedHashMap<" .. subtypename_new(var, var.k) .. subtypename_new() .. subtypename_new(var, var.v) .. ">(#(var.cap)))" end,
})
typedef.bean = merge(typedef.octets,
{
	import = {},
	type = function(var) return var.type end,
	type_i = function(var) return var.type end,
	type_o = function(var) return var.type end,
	subtypeid = 2,
	final = "final ",
	field = "",
	fieldget = "",
	new = function(var) return "\t\t#(var.name) = new " .. var.type .. "();\n" end,
	init = function(var) return "this.#(var.name) = (#(var.name) != null ? #(var.name).clone() : new " .. var.type .. "())" end,
	reset = "#(var.name).reset()",
	assign = "this.#(var.name).assign(b.#(var.name))",
	set = "",
	getsafe = [[

		/** @return #(var.comment1) */
		public #(var.type).Safe get#(var.name_u)()
		{
			return _bean.#(var.name).safe(this);
		}

		/** @return #(var.comment1) */
		@Deprecated
		public #(var.type) unsafe#(var.name_u)()
		{
			return _bean.#(var.name);
		}
]],
	setsafe = "",
	marshal = function(var)
		return var.id < 63 and
			string.format([[
		{
			int n = s.size();
			this.#(var.name).marshal(s.marshal1((byte)0x%02x));
			if(s.size() - n < 3) s.resize(n);
		}
]], var.id * 4 + 2) or
			string.format([[
		{
			int n = s.size();
			this.#(var.name).marshal(s.marshal2(0x%04x));
			if(s.size() - n < 3) s.resize(n);
		}
]], 0xfe00 + var.id - 63)
	end,
	unmarshal = "\t\t\tcase #(var.id): s.unmarshalBean(this.#(var.name), t); break;\n",
	unmarshal_kv = function(var, kv, t) if kv then return "s.unmarshalBeanKV(new " .. typename(var, var[kv]) .. "(), " .. t .. ")" end end,
	compareto = "this.#(var.name).compareTo(b.#(var.name))",
	tojson = "\t\tthis.#(var.name).toJson(s.append(\"\\\"#(var.name)\\\":\")).append(',');\n",
	tolua = "\t\tthis.#(var.name).toLua(s.append(\"#(var.name)=\")).append(',');\n",
})
typedef.ref = merge(typedef.bean,
{
	final = "",
	field = "\tprivate static Field FIELD_#(var.name);\n",
	fieldget = "\t\t\tFIELD_#(var.name) = c.getDeclaredField(\"#(var.name)\"); FIELD_#(var.name).setAccessible(true);\n",
	new = "\t\t#(var.name) = null;\n",
	init = "this.#(var.name) = #(var.name)",
	reset = "#(var.name) = null",
	assign = "this.#(var.name) = b.#(var.name)",
	set = [[

	/** @param #(var.name) #(var.comment1) */
	public void set#(var.name_u)(#(var.type) #(var.name))
	{
		this.#(var.name) = #(var.name);
	}
]],
	getsafe = [[

		/** @return #(var.comment1) */
		public #(var.type) get#(var.name_u)()
		{
			return _bean.#(var.name);
		}
]],
	setsafe = function() return [[

		/** @param #(var.name) #(var.comment1) */
		public void set#(var.name_u)(#(var.type) #(var.name))
		{
			if(initSContext()) _sCtx.addOnRollback(new SBase.SObject(_bean, FIELD_#(var.name), _bean.#(var.name)));
			_bean.#(var.name) = #(var.name);
		}
]] end,
	marshal = "",
	unmarshal = "",
	unmarshal_kv = "",
	hashcode = "0",
	compareto = "0",
	tojson = "",
	tolua = "",
})
typedef.boolean = typedef.bool
typedef.integer = typedef.int
typedef.binary = typedef.octets
typedef.bytes = typedef.octets
typedef.data = typedef.octets
typedef.array = typedef.vector
typedef.arraydeque = typedef.deque
typedef.arraylist = typedef.vector
typedef.linkedlist = typedef.list
typedef.set = typedef.hashset
typedef.linkedset = typedef.linkedhashset
typedef.map = typedef.hashmap
typedef.linkedmap = typedef.linkedhashmap

local function trim(s)
	return s:gsub("[%c%s]+", "")
end
local function do_var(var)
	if type(var.id) ~= "number" then var.id = -1 end
	if var.id < -1 or var.id > 190 then error("ERROR: var.id=" .. var.id .. " must be in [1, 190]") end
	var.import = {}
	var.id3 = string.format("%3d", var.id)
	var.name = trim(var.name)
	var.type = trim(var.type)
	if var.comment and #var.comment > 0 then
		var.comment1 = var.comment:gsub("%c", " ")
		var.comment2 = " // " .. var.comment1
	else
		var.comment1 = ""
		var.comment2 = ""
	end
	if type(var.value) == "string" then var.value = "\"" .. var.value .. "\"" end
	var.value = var.value and " = " .. var.value or ""
	local basetype
	basetype, var.k, var.v, var.cap = var.type:match "^%s*([%w_%.]+)%s*<?%s*([%w_%.]*)%s*,?%s*([%w_%.]*)%s*>?%s*%(?%s*([%w_%.]*)%s*%)?%s*$"
	if not var.cap then var.cap = "" end
	local def = typedef[basetype]
	if not def and typedef[lower(basetype)] then basetype = lower(basetype) def = typedef[basetype] end
	if var.k and not typedef[var.k] and typedef[lower(var.k)] then var.k = lower(var.k) end
	if var.v and not typedef[var.v] and typedef[lower(var.v)] then var.v = lower(var.v) end
	if not def then def = var.id > 0 and typedef.bean or typedef.ref end
	if type(def) == "table" then
		for k, v in pairs(def) do
			if type(v) == "function" then v = v(var) end
			var[k] = v
		end
	else
		error("ERROR: unknown type: " .. var.type .. " => " .. basetype)
	end
end
local function code_conv(code, prefix, t)
	return code:gsub("#%(" .. prefix .. "%.([%w_]+)%)", function(name) return t[name] end)
end
local uid_used = {}
local function gen_uid(beanname, s)
	local h = 0
	for i = 1, #s do
		h = h % 0x10000000000 * 4093 + 1 + s:byte(i)
	end
	h = string.format("0xbeac%04x%08xL", math.floor(h / 0x100000000) % 0x10000, h % 0x100000000)
	if uid_used[h] then
		error("ERROR: hash collisions in gen_uid(" .. uid_used[h] .. ", " .. beanname .. "): " ..  h)
	end
	uid_used[h] = beanname
	return h
end

local name_code = {} -- bean name => bean code
local type_bean = {} -- bean type => bean
local name_bean = {} -- bean name => bean
local handlers = {} -- selected handler name => handler path
local has_handler -- any selected handler?
local all_handlers = {} -- all handlers name => true
local hdl_names = {} -- handler name => {bean names}
local bean_order = {} -- defined order => bean name
local tables = { imports = { ["java.util.HashMap"] = true, ["jane.core.Bean"] = true } }
function handler(hdls)
	if not arg[2] then error("ERROR: arg[2] must be handler name(s)") end
	for hdlname in arg[2]:gmatch("([%w_%.]+)") do
		local hdl = hdls[hdlname]
		if not hdl then error("ERROR: not found or unknown handler name: " .. hdlname) end
		for hdlname, hdlpath in pairs(hdl) do
			if type(handlers[hdlname]) ~= "string" then
				handlers[hdlname] = hdlpath
				has_handler = true
			end
		end
	end
	for _, v in pairs(hdls) do
		for k in pairs(v) do
			all_handlers[k] = true
		end
	end
end
local function bean_common(bean)
	bean.name = trim(bean.name)
	if bean.name:find("[^%w_]") or typedef[bean.name] or bean.name == "AllBeans" or bean.name == "AllTables" then error("ERROR: invalid bean.name: " .. bean.name) end
	if name_code[lower(bean.name)] then error("ERROR: duplicated bean.name: " .. bean.name) end
	if bean.handlers and type_bean[bean.type] then error("ERROR: duplicated bean.type: " .. bean.type) end
	if type(bean.type) ~= "number" then bean.type = 0 end
	for name in (bean.handlers or ""):gmatch("([%w_%.]+)") do
		if not all_handlers[name] then error("ERROR: not defined handler: " .. name) end
		hdl_names[name] = hdl_names[name] or {}
		hdl_names[name][#hdl_names[name] + 1] = bean.name
	end
	type_bean[bean.type] = bean
	name_bean[bean.name] = bean
	bean.comment = bean.comment and #bean.comment > 0 and "\n/**\n * " .. bean.comment:gsub("\n", "<br>\n * ") .. "\n */" or ""
	bean_order[#bean_order + 1] = bean.name
end
local function bean_const(code)
	return code:gsub("public  /%*", "private /*"):
		gsub("\t@Override\n\tpublic void assign%(.-\n\t}\n\n", ""):
		gsub("\t/%*%* [^\n]*\n\tpublic void set.-\n\t}\n\n", ""):
		gsub("\n\tpublic static final class Safe.-}\n\t}\n", ""):
		gsub("import java%.lang%.reflect%.Field;\n", ""):
		gsub("import jane%.core%.DynBean;\n", ""):
		gsub("import jane%.core%.SBase;\n", ""):
		gsub("import jane%.core%.SContext;\n", ""):
		gsub("\tprivate static Field .-\n", ""):
		gsub("\tstatic\n.-\n\t}\n\n", ""):
		gsub("%*/ String ", "*/ final String "):
		gsub("%*/ Octets ", "*/ final Octets "):
		gsub("\tpublic <B extends Bean<B>> void marshal.-\n\t}\n\n", ""):
		gsub("\tpublic <B extends Bean<B>> Bean<B> unmarshal.-\n\t}\n\n", ""):
		gsub("\tpublic DynBean unmarshal.-\n\t}\n\n", ""):
		gsub("\n\t@Override\n\tpublic Safe safe.-\n\t}\n", ""):
		gsub("\t@Override\n\tpublic void reset%(.-\n\t}", [[
	@Deprecated
	@Override
	public void reset()
	{
		throw new UnsupportedOperationException();
	}]]):
		gsub("\t@Override\n\tpublic OctetsStream unmarshal%(.-\n\t}", [[
	@Deprecated
	@Override
	public OctetsStream unmarshal(OctetsStream s) throws MarshalException
	{
		throw new UnsupportedOperationException();
	}]])
end
local function get_imports(import)
	local imports = {}
	for k in pairs(import) do
		imports[#imports + 1] = k
	end
	table.sort(imports, function(a, b) return a:gsub("^java%.", ".") < b:gsub("^java%.", ".") end)
	return concat(imports, ";\nimport ")
end
function bean(bean)
	bean_common(bean)

	bean.import = { ["jane.core.Bean"] = true, ["jane.core.MarshalException"] = true, ["jane.core.OctetsStream"] = true, ["jane.core.SContext"] = true }
	local vartypes = { bean.name }
	local id_used = {}
	for _, var in ipairs(bean) do
		do_var(var)
		if var.id > 0 then
			if id_used[var.id] then error("ERROR: duplicated var.id: " .. var.id .. " in bean: " .. bean.name) end
			id_used[var.id] = true
			vartypes[#vartypes + 1] = var.type
			for _, v in ipairs(var.import) do
				bean.import[v] = true
			end
		end
	end
	if bean.poolsize and bean.poolsize > 0 then
		bean.import["jane.core.BeanPool"] = true
		bean.pool_def = "\n\tpublic  static final BeanPool<" .. bean.name .. "> BEAN_POOL = new BeanPool<" .. (jdk7 and "" or bean.name) .. ">(BEAN_STUB, " .. bean.poolsize .. ");"
		bean.pool_func = [[

	@Override
	public ]] .. bean.name .. [[ alloc()
	{
		return BEAN_POOL.alloc();
	}

	@Override
	public void free()
	{
		BEAN_POOL.free(this);
	}
]]
	else
		bean.pool_def = ""
		bean.pool_func = ""
	end

	if not bean.maxsize then bean.maxsize = 0x7fffffff end
	if not bean.initsize then bean.initsize = 0 end
	if type(bean.initsize) == "number" and bean.initsize > 0x100000 then print("WARNING: bean.initsize = " .. bean.initsize .. " > 1MB (bean.name:" .. bean.name .. ")") end
	bean.imports = get_imports(bean.import)
	bean.uid = gen_uid(bean.name, concat(vartypes))

	local code = template_bean:gsub("#{#(.-)#}#", function(body)
		local subcode = {}
		for _, var in ipairs(bean) do
			if var.id == -1 then subcode[#subcode + 1] = code_conv(body, "var", var) end
		end
		return concat(subcode)
	end):gsub("#%(#(.-)#%)#", function(body)
		local subcode = {}
		for _, var in ipairs(bean) do
			if var.id >= 0 then subcode[#subcode + 1] = code_conv(code_conv(body, "var", var), "var", var) end
		end
		local code = concat(subcode)
		return code:sub(-2, -1) ~= ", " and code or code:sub(1, -3)
	end)

	bean.param_warning = (#vartypes > 1 and "" or "/** @param b unused */\n\t")
	code = code_conv(code, "bean", bean):
		gsub(#vartypes > 1 and "#[<>]#" or "#<#(.-)#>#", ""):
		gsub("\n\n\tstatic\n\t{\n\t\ttry\n\t\t{\n\t\t\tClass<.-> c = .-%.class;\n\t\t}\n\t\tcatch%(Exception e%)\n\t\t{\n\t\t\tthrow new Error%(e%);\n\t\t}\n\t}", ""):
		gsub("int h = (%(int%)serialVersionUID;)\n\t\treturn h;", "return %1"):
		gsub("\t+/%*%* @return  %*/\n", ""):
		gsub("\t+/%*%* @param [%w_]+  %*/\n", ""):
		gsub("\t+/%*%*  %*/\n", ""):
		gsub("\n\t{\n\n\t\t", "\n\t{\n\t\t"):
		gsub("\r", "")
	if not code:find("\tprivate static Field ") then
		code = code:gsub("import java.lang.reflect.Field;\n", "")
	end
	if bean.const then code = bean_const(code) end
	local c, n = code:gsub([[
	static
	{
		try
		{
			Class<[%w_%.]+> c = [%w_%.]+%.class;
		}
		catch%(Exception e%)
		{
		}
	}

]], "")
	if n > 0 then code = c:gsub("import java.lang.reflect.Field;\n", "") end
	name_code[bean.name] = code
	name_code[lower(bean.name)] = code
end

local outpath = (arg[4] or "."):gsub("\\", "/")
if outpath:sub(-1, -1) ~= "/" then outpath = outpath .. "/" end
local function checksave(fn, d, change_count, pattern, typename)
	local f = open(fn, "rb")
	if f then
		local s = f:read "*a"
		f:close()
		if change_count > 0 then
			d = s:gsub("\n\t/%*\\.-\n\t\\%*/", d:gmatch("\n\t/%*\\.-\n\t\\%*/"), change_count):gsub(pattern, typename, 1)
		end
		if s == d then d = nil else print(" * " .. fn) end
	else
		print("+  " .. fn)
	end
	if d then
		f = open(fn, "wb")
		if not f then error("ERROR: can not create file: " .. fn) end
		f:write(d)
		f:close()
	end
end

local saved = {}
local function savebean(beanname)
	if saved[beanname] then return end
	saved[beanname] = true
	if not name_code[beanname] then error("ERROR: unknown bean: " .. beanname) end
	checksave(outpath .. namespace .. "/bean/" .. beanname .. ".java", name_code[beanname], 0)
	bean_order[beanname] = true
	for _, var in ipairs(name_bean[beanname]) do
		if name_bean[var.type] then savebean(var.type) end
		if name_bean[var.k] then savebean(var.k) end
		if name_bean[var.v] then savebean(var.v) end
	end
end
function rpc(bean)
	bean_common(bean)
	bean.uid = gen_uid(bean.name, name_bean[bean.arg].uid .. name_bean[bean.res].uid)
	name_code[bean.name] = code_conv(template_rpcbean, "bean", bean):gsub("\r", "")
	name_code[lower(bean.name)] = name_code[bean.name]
	savebean(bean.arg)
	savebean(bean.res)
end

local key_conv = { int = "Integer", integer = "Integer", Integer = "Integer", long = "Long", Long = "Long", float = "Float", Float = "Float", double = "Double", Double = "Double",
					string = "String", String = "String", binary = "Octets", bytes = "Octets", data = "Octets", octets = "Octets", Octets = "Octets" }
function dbt(table)
	if not handlers.dbt then return end
	local key_type = key_conv[table.key]
	if key_type then
		if key_type == "Octets" then tables.imports["jane.core.Octets"] = true end
		table.table = "Table"
		table.key = key_type
		if table.memory then table.keys = "null"
		elseif key_type == "String" then table.keys = "\"\""
		elseif key_type == "Octets" then table.keys = "new Octets()"
		else table.keys = "new " .. key_type .. "(0)" end
		table.keyg = "null"
		table.comma = ", "
		tables.imports["jane.core.Table"] = true
	elseif table.key == "id" then
		table.table = "TableLong"
		table.key = ""
		table.keys = ""
		table.keyg = "null"
		table.comma = ""
		tables.imports["jane.core.TableLong"] = true
	else
		table.table = "Table"
		table.keys = not table.memory and "#(table.key).BEAN_STUB" or "null"
		table.keyg = table.keys
		table.comma = ", "
		tables.imports["jane.core.Table"] = true
		savebean(table.key)
	end
	table.values = table.memory and "null" or "#(table.value).BEAN_STUB"
	table.lock = table.lock or ""
	if table.comment and #table.comment > 0 then table.comment = "/**\n\t * " .. table.comment:gsub("\n", "<br>\n\t * ") .. "\n\t */\n\t" end
	tables[#tables + 1] = table
	savebean(table.value)
end

if not arg[3] then error("ERROR: arg[3] must be input allbeans.lua") end
dofile(arg[3])

local bean_count = 0
checksave(outpath .. namespace .. "/bean/AllBeans.java", (template_allbeans:gsub("#%[#(.-)#%]#", function(body)
	local subcode = {}
	for hdlname, hdlpath in pairs(handlers) do
		local names = hdl_names[hdlname] or {}
		local hdl = { name = hdlname, path = tostring(hdlpath), count = #names }
		subcode[#subcode + 1] = code_conv(body:gsub("#%(#(.-)#%)#", function(body)
			local subcode2 = {}
			for _, name in ipairs(names) do
				local bean = name_bean[name]
				savebean(bean.name)
				subcode2[#subcode2 + 1] = code_conv(body, "bean", bean)
				if type(hdlpath) == "string" then
					if bean.type <= 0 or bean.type > 0x7fffffff then error("ERROR: invalid bean.type: " .. tostring(bean.type) .. " (bean.name: " .. bean.name .. ")") end
					if not bean.arg then
						checksave(outpath .. hdlpath:gsub("%.", "/") .. "/" .. bean.name .. "Handler.java", code_conv(code_conv(template_bean_handler:gsub("#%(#(.-)#%)#", function(body)
							local subcode3 = {}
							for _, var in ipairs(bean) do
								subcode3[#subcode3 + 1] = code_conv(body, "var", var)
							end
							return concat(subcode3)
						end), "hdl", hdl), "bean", bean):gsub("\r", ""), 1, "(%s+class%s+" .. bean.name .. "Handler%s+extends%s+BeanHandler%s*<)[%w_%.%s]+>", "%1" .. bean.name .. ">")
					else
						local bean_sub
						local bean_arg, bean_res = name_bean[bean.arg], name_bean[bean.res]
						checksave(outpath .. hdlpath:gsub("%.", "/") .. "/" .. bean.name .. "Handler.java", code_conv(code_conv(code_conv(code_conv(template_rpc_handler:gsub("#%(#(.-)#%)#", function(body)
							bean_sub = bean_sub and bean_res or bean_arg
							local subcode3 = {}
							for _, var in ipairs(bean_sub) do
								subcode3[#subcode3 + 1] = code_conv(body, "var", var)
							end
							return concat(subcode3)
						end), "hdl", hdl), "bean", bean), "bean_arg", bean_arg), "bean_res", bean_res):gsub(bean_arg ~= bean_res and "#[<>]#" or "#%<#(.-)#%>#", ""):
							gsub("\r", ""), 2, "(%s+class%s+" .. bean.name .. "Handler%s+extends%s+RpcHandler%s*<)[%w_%.%s]+,[%w_%.%s]+>", "%1" .. bean_arg.name .. ", " .. bean_res.name .. ">")
					end
				end
			end
			return concat(subcode2)
		end), "hdl", hdl)
		if type(hdlpath) ~= "string" then subcode[#subcode] = "" end
	end
	return concat(subcode)
end):gsub("#%(#(.-)#%)#", function(body)
	local subcode = {}
	for _, beanname in ipairs(bean_order) do
		if bean_order[beanname] then
			local bean = name_bean[beanname]
			if bean.type > 0 then
				subcode[#subcode + 1] = code_conv(body, "bean", bean)
				bean_count = bean_count + 1
			end
		end
	end
	return concat(subcode)
end)):gsub(has_handler and "#[<>]#" or "#%<#(.-)#%>#", ""):gsub("#%(bean.count%)", bean_count):gsub("\r", ""), 0)

tables.count = #tables
tables.imports["jane.core.DBManager"] = true
tables.imports = get_imports(tables.imports)
checksave(outpath .. namespace .. "/bean/AllTables.java", (code_conv(template_alltables:gsub("#%(#(.-)#%)#", function(body)
	local subcode = {}
	local names = {}
	local ids = {}
	for _, table in ipairs(tables) do
		if names[table.name] then error("ERROR: duplicated table.name: " .. table.name) end
		if ids[table.id] then error("ERROR: duplicated table.id: " .. table.id) end
		if table.id < 1 or table.id > 0x7fffffff then error("ERROR: invalid table.id: " .. table.id) end
		names[table.name] = true
		ids[table.id] = true
		subcode[#subcode + 1] = code_conv(code_conv(body, "table", table), "table", table)
	end
	return concat(subcode)
end), "tables", tables):gsub(#tables > 0 and "#[<>]#" or "#<#(.-)#>#", ""):gsub("\r", "")), 0)

print "done!"
